/*
 * Copyright (C) 2019-2020 Alibaba Group Holding Limited
 */
#include <stdio.h>
#include <aos/aos.h>
#include <aos/ble.h>
#include <aos/gatt.h>
#include <yoc/partition.h>
#include "drv/gpio.h"
#include "pin_name.h"
#include "pinmux.h"
#include "drv/timer.h"
#include "yoc/hids.h"
#include "yoc/bas.h"
#include "yoc/dis.h"
#include <alertor_server.h>
#include "aos/hal/pcm.h"
#include "pin_name.h"

#define TAG "ALERTOR"
static partition_t g_pcm_partition_handle = -1;
static partition_info_t *g_pcm_partition_info = NULL;

#if defined(BOARD_LE5010_EVB)
#include "pwm_config.h"
#define MIN(a, b) (a)<(b)? (a) : (b)
#endif

aos_audio_pcm_device_t g_pcm_dev= {
    /* rate, channels(B00000011), period_size(20ms), period_count(100ms), format */
    {8000, 3, 20, 100, AOS_PCM_FORMAT_S8},
    AOS_AUDIO_PCM_DIR_OUT,
    0,
    0,
    NULL,
};

extern const char *bt_hex_real(const void *buf, size_t len);

#define ALERTOR_PACKET_SIZE (256)
#define ALERTOR_DATA_HEAD     0xC3

#define MANUFACTURER_NAME "PINGTOUGE"
#define MODEL_NUMBER "MODE_KEYBOARD"
#define SERIAL_NUMBER "00000001"
#define HW_REV "0.0.1"
#define FW_REV "0.0.2"
#define SW_REV "0.0.3"

static pnp_id_t pnp_id = {
    VEND_ID_SOURCE_USB,
    0x1915,
    0xEEEE,
    0x0001,
};

static dis_info_t dis_info = {
    MANUFACTURER_NAME,
    MODEL_NUMBER,
    SERIAL_NUMBER,
    HW_REV,
    FW_REV,
    SW_REV,
    NULL,
    NULL,
    &pnp_id,
};

typedef struct  _SPECIAL_KEY_VALUE_ {
    uint8_t    Left_Ctrl        : 1;
    uint8_t    Left_Shift       : 1;
    uint8_t    Left_Alt         : 1;
    uint8_t    Left_Gui         : 1;
    uint8_t    Right_Ctrl       : 1;
    uint8_t    Right_Shift      : 1;
    uint8_t    Right_Alt        : 1;
    uint8_t    Right_Gui        : 1;
} SPECIAL_KEY_VALUE_BIT;

typedef union {
    SPECIAL_KEY_VALUE_BIT  bits;
    uint8_t           data;
} SPCL_KEY;

typedef struct press_key  {
    SPCL_KEY  keydata;
    uint8_t     Rsv;
    uint8_t   Code1;
    uint8_t   Code2;
    uint8_t   Code3;
    uint8_t   Code4;
    uint8_t   Code5;
    uint8_t   Code6;
} press_key_data;

static uint8_t report_map[] = {
0x05, 0x0C, 					// Usage Page (Consumer)  ---- 0x0c
0x09, 0x01, 					// Usage (Consumer Control)
0xA1, 0x01, 					// Collection (Application)
0x09, 0xCD, 					//	   Usage (Play/Pause)
0x09, 0x30, 					//	   
0x09, 0xB5, 					//	   Usage (Scan Next Track)
0x09, 0xB6, 					//	   Usage (Scan Previous Track)
0x09, 0xEA, 					//	   Usage (Volume Down)
0x09, 0xE9, 					//	   Usage (Volume Up)
0x09, 0x30, 					//	   Power 
0x09, 0x41, 					//	   Usage (Menu Pick)

0x15, 0x00, 					//	   Logical minimum (0)
0x25, 0x01, 					//	   Logical maximum (1)
0x75, 0x01, 					//	   Report Size (1)
0x95, 0x08, 					//	   Report Count (8)
0x81, 0x02, 					//
0xC0 ,

};

static uint8_t report_output_data[1] = {
    0x00,
};

press_key_data  send_data;

struct ble_alertor_rx_t {
    uint8_t rx_buf[ALERTOR_PACKET_SIZE];
    uint16_t rx_len;
};

static struct ble_alertor_rx_t ble_alertor_rx = {0};

static struct ble_alertor_ctrl_t {
    hids_handle_t hids_handle;
    bas_handle_t bas_handle;
    dis_handle_t dis_handle;
    bas_t bas;

    int16_t paired_handle;
    alertor_state_en st;
    uint8_t  timeout;
    aos_timer_t timer;
    alertor_event_callback_t cb;
} ble_alertor_ctrl;

#define YOC_ALERTOR_SERVICE_UUID                                                                      \
    UUID128_DECLARE(0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0x00,  \
                    0x10, 0x00, 0x00)

#define YOC_ALERTOR_RX_UUID                                                                           \
    UUID128_DECLARE(0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0x01,  \
                    0x10, 0x00, 0x00)

#define YOC_ALERTOR_TX_UUID                                                                           \
    UUID128_DECLARE(0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0x02,  \
                    0x10, 0x00, 0x00)

static int16_t g_yoc_alertor_handle  = -1;
static int16_t g_conn_hanlde      = -1;
static int16_t g_tx_ccc_value     = 0;

static char rx_char_des[] = "YoC ALERTOR RX";
static char tx_char_des[] = "YoC ALERTOR TX";

enum {
    YOC_ALERTOR_IDX_SVC,

    YOC_ALERTOR_IDX_RX_CHAR,
    YOC_ALERTOR_IDX_RX_VAL,
    YOC_ALERTOR_IDX_RX_DES,

    YOC_ALERTOR_IDX_TX_CHAR,
    YOC_ALERTOR_IDX_TX_VAL,
    YOC_ALERTOR_IDX_TX_CCC,
    YOC_ALERTOR_IDX_TX_DES,

    YOC_ALERTOR_IDX_MAX,
};

static gatt_service                    uart_service;

static gatt_attr_t uart_attrs[YOC_ALERTOR_IDX_MAX] = {
    /* ALERTOR服务Primary Service定义 */
    [YOC_ALERTOR_IDX_SVC] = GATT_PRIMARY_SERVICE_DEFINE(YOC_ALERTOR_SERVICE_UUID),
/* ALERTOR服务RX特征定义-无需回复的写入属性 */
    [YOC_ALERTOR_IDX_RX_CHAR] = GATT_CHAR_DEFINE(YOC_ALERTOR_RX_UUID, GATT_CHRC_PROP_WRITE_WITHOUT_RESP),
    /* ALERTOR服务RX特征属性定义 -读属性许可 | 写属性许可*/
    [YOC_ALERTOR_IDX_RX_VAL] =
    GATT_CHAR_VAL_DEFINE(YOC_ALERTOR_RX_UUID, GATT_PERM_READ | GATT_PERM_WRITE | GATT_PERM_READ_AUTHEN),
    /* ALERTOR服务RX特征用户描述符定义 -读属性许可*/
    [YOC_ALERTOR_IDX_RX_DES] = GATT_CHAR_CUD_DEFINE(rx_char_des, GATT_PERM_READ),
/* ALERTOR服务TX特征定义 -通知属性（允许未经接受的通知特性）| 读属性 */
    [YOC_ALERTOR_IDX_TX_CHAR] =
    GATT_CHAR_DEFINE(YOC_ALERTOR_TX_UUID, GATT_CHRC_PROP_NOTIFY),
/* ALERTOR服务TX特征定义  读属性许可*/
    [YOC_ALERTOR_IDX_TX_VAL] = GATT_CHAR_VAL_DEFINE(YOC_ALERTOR_TX_UUID, GATT_PERM_READ | GATT_PERM_WRITE | GATT_PERM_READ_AUTHEN),
/* ALERTOR服务TX特征CCC属性(客户端特征配置,可用于客户端配置触发的条件)定义 */
    [YOC_ALERTOR_IDX_TX_CCC] = GATT_CHAR_CCC_DEFINE(),
/* ALERTOR服务TX特征用户描述GATT特征符定义  -读属性许可*/
    [YOC_ALERTOR_IDX_TX_DES] = GATT_CHAR_CUD_DEFINE(tx_char_des, GATT_PERM_READ),
};

static int ble_audio_play(uint8_t * buffer, int length)
{
    aos_audio_pcm_close(&g_pcm_dev);
    aos_audio_pcm_open(&g_pcm_dev);
    aos_audio_pcm_write(&g_pcm_dev, buffer, length);

    return 0;
}

static void ble_alertor_status_change(int state)
{
    LOGI(TAG,  "%d->%d", ble_alertor_ctrl.st, state);

    ble_alertor_ctrl.st = state;
	/* ALERTOR状态通过回调函数同步到APP应用层 */
    if (ble_alertor_ctrl.cb) {
        ble_alertor_ctrl.cb(state);
    }
}

static void ble_alertor_reset()
{
    aos_timer_stop(&ble_alertor_ctrl.timer);
    memset(&ble_alertor_rx, 0, sizeof(ble_alertor_rx));
}

static void ble_alertor_reset_rx()
{
    memset(&ble_alertor_rx, 0, sizeof(ble_alertor_rx));
}

static void ble_alertor_change_timeout()
{
    aos_timer_change(&ble_alertor_ctrl.timer, ble_alertor_ctrl.timeout * 1000);
    aos_timer_start(&ble_alertor_ctrl.timer);
}

static void conn_change(ble_event_en event, void *event_data)
{
    evt_data_gap_conn_change_t *e = (evt_data_gap_conn_change_t *)event_data;

    if (e->connected == CONNECTED) {
        g_conn_hanlde = e->conn_handle;
    } else {
        g_conn_hanlde = -1;
        ble_alertor_reset();
        ble_alertor_status_change(ALERTOR_ST_IDLE);
    }
}

static void event_char_write(ble_event_en event, void *event_data)
{
    evt_data_gatt_char_write_t *e            = (evt_data_gatt_char_write_t *)event_data;
    int16_t                     hande_offset = 0;
    if (g_conn_hanlde != e->conn_handle) {
        return;
    }

    if (e->char_handle > g_yoc_alertor_handle && e->char_handle < g_yoc_alertor_handle + YOC_ALERTOR_IDX_MAX) {
        hande_offset = e->char_handle - g_yoc_alertor_handle;
    } else {
        return;
    }
    LOGI(TAG, "e->char_handle ,g_yoc_alertor_handle %d %d %s\n", e->char_handle , g_yoc_alertor_handle, bt_hex_real(e->data, e->len));

    switch (hande_offset) {
        /* 数据接收处理 */
        case YOC_ALERTOR_IDX_RX_VAL: {
            ble_alertor_change_timeout();
            if (e->data[0] != ALERTOR_DATA_HEAD ||
                e->len != sizeof(alertor_ble_data_t)) {
                LOGE(TAG, "Err Data %s", bt_hex_real(e->data, e->len));
                return;
            }

            memset(ble_alertor_rx.rx_buf, 0, ALERTOR_PACKET_SIZE);
            memcpy(ble_alertor_rx.rx_buf, e->data, e->len);
            ble_alertor_rx.rx_len = e->len;
            ble_alertor_status_change(ALERTOR_ST_WORKING);
        }
        break;
    }

    return;
}

static void event_char_read(ble_event_en event, void *event_data)
{
    evt_data_gatt_char_read_t *e            = (evt_data_gatt_char_read_t *)event_data;
    int16_t                    hande_offset = 0;

    if (g_conn_hanlde == e->conn_handle) {
        if (e->char_handle > g_yoc_alertor_handle) {
            hande_offset = e->char_handle - g_yoc_alertor_handle;
        } else {
            return;
        }
        // LOGI(TAG,  "e->data %d\n", *e->data);
        switch (hande_offset) {
            case YOC_ALERTOR_IDX_RX_VAL:
                memcpy(e->data, ble_alertor_rx.rx_buf, MIN(e->len, sizeof(ble_alertor_rx.rx_buf)));
                e->len = MIN(e->len, sizeof(ble_alertor_rx.rx_buf));
                return;

            case YOC_ALERTOR_IDX_RX_DES:
                memcpy(e->data, rx_char_des, MIN(e->len, strlen(rx_char_des)));
                e->len = MIN(e->len, strlen(rx_char_des));
                return;

            case YOC_ALERTOR_IDX_TX_DES:
                memcpy(e->data, tx_char_des, MIN(e->len, strlen(tx_char_des)));
                e->len = MIN(e->len, strlen(tx_char_des));
                return;
        }
    }

    return;
}

static void event_char_ccc_change(ble_event_en event, void *event_data)
{
    evt_data_gatt_char_ccc_change_t *e = (evt_data_gatt_char_ccc_change_t *)event_data;

    if (e->char_handle != g_yoc_alertor_handle + YOC_ALERTOR_IDX_TX_CCC) {
        return;
    }

    g_tx_ccc_value                     = e->ccc_value;

    LOGI(TAG,  "ccc %d %d\n", e->char_handle, e->ccc_value);
}

static void event_smp_complete(ble_event_en event, void *event_data)
{
    evt_data_smp_pairing_complete_t *e = (evt_data_smp_pairing_complete_t *)event_data;

    if (e->err == 0) {
        ble_alertor_ctrl.paired_handle = e->conn_handle;
    } else {
        ble_alertor_status_change(ALERTOR_ST_DISCONNECT);
    }

    LOGI(TAG,  "pairing %s with %s!!!", e->err ? "FAIL" : "SUCCESS", bt_hex_real(e->peer_addr.val, 6));
}

static void mtu_exchange(ble_event_en event, void *event_data)
{
    evt_data_gatt_mtu_exchange_t *e = (evt_data_gatt_mtu_exchange_t *)event_data;

    if (e->err == 0) {
        LOGI(TAG,  "mtu get %d", ble_stack_gatt_mtu_get(e->conn_handle));
    } else {
        LOGE(TAG, "mtu exchange fail, %d", e->err);
    }
}

static int event_callback(ble_event_en event, void *event_data)
{

    switch (event) {
        case EVENT_GAP_CONN_CHANGE:
            conn_change(event, event_data);
            break;

        case EVENT_GATT_CHAR_READ:
            event_char_read(event, event_data);
            break;

        case EVENT_GATT_CHAR_WRITE:
            event_char_write(event, event_data);
            break;

        case EVENT_GATT_CHAR_CCC_CHANGE:
            event_char_ccc_change(event, event_data);
            break;

        case EVENT_GATT_MTU_EXCHANGE:
            mtu_exchange(event, event_data);
            break;

        case EVENT_SMP_PAIRING_COMPLETE: //18
            event_smp_complete(event, event_data);
            break;

        case EVENT_SMP_PAIRING_CONFIRM:
            ble_stack_smp_pairing_confirm(g_conn_hanlde);
            break;

        default:
            break;
    }

    return 0;
}


static ble_event_cb_t ble_alertor_cb = {
    .callback = event_callback,
};

/* 逻辑处理主函数，数据发送 */
void ble_alertor_process()
{
    // LOGI(TAG, "st %d", ble_alertor_ctrl.st);

    switch (ble_alertor_ctrl.st) {
        case ALERTOR_ST_WORKING: {
            if (g_conn_hanlde == -1) {
                return;
            }

            alertor_ble_data_t *alertor_data = (alertor_ble_data_t *)ble_alertor_rx.rx_buf;
            //LOGI(TAG, "recv %x %x %x", alertor_data->head, alertor_data->cmd, alertor_data->param);

            switch (alertor_data->cmd) {
                case ALERTOR_CMD0: {
                    if (alertor_data->param == ALERTOR_SHEFANG) {
                        //设防
                        if (g_pcm_partition_info != NULL) {
                            LOGI(TAG, "pcm play shefang");
                            ble_audio_play((unsigned char *)(g_pcm_partition_info->base_addr +g_pcm_partition_info->start_addr), 18408);
                        }

                    } else if (alertor_data->param == ALERTOR_UNLOCK) {
                        //解锁
                        if (g_pcm_partition_info != NULL) {
                            LOGI(TAG, "pcm play unlock");
                            ble_audio_play((unsigned char *)(g_pcm_partition_info->base_addr +g_pcm_partition_info->start_addr), 800);
                        }

                    } else if (alertor_data->param == ALERTOR_FIND) {
                        //寻车
                        if (g_pcm_partition_info != NULL) {
                            LOGD(TAG, "pcm play find");
                            ble_audio_play((unsigned char *)(g_pcm_partition_info->base_addr +g_pcm_partition_info->start_addr), 800);
                        }
                    } else if (alertor_data->param == ALERTOR_START) {
                        //启动
                        if (g_pcm_partition_info != NULL) {
                            LOGI(TAG, "pcm play start");
                            ble_audio_play((unsigned char *)(g_pcm_partition_info->base_addr +g_pcm_partition_info->start_addr), 800);
                        }
                    }
                    break;
                }
            
                case ALERTOR_CMD_SEN_SET: {
                    if (alertor_data->param >= 1 && alertor_data->param <= 4 ) {
                        //根据alertor_data->param设置灵敏度
                    }
                    break;
                }
            
                case ALERTOR_CMD_DATA_ACK: {
                    if (alertor_data->param == 1) {
                        //数据ACK
                    }
                    break;
                }
            
                case ALERTOR_CMD_SEAT_LOCK: {

                    if (alertor_data->param == ALERTOR_SEAT_LOCK) {
                        //鞍座锁开
                        if (g_pcm_partition_info != NULL) {
                            LOGI(TAG, "pcm play seat unlock");
                            ble_audio_play((unsigned char *)(g_pcm_partition_info->base_addr +g_pcm_partition_info->start_addr), 800);
                        }
                    } else if (alertor_data->param == ALERTOR_SEAT_UNLOCK) {
                        //鞍座锁关
                        if (g_pcm_partition_info != NULL) {
                            LOGI(TAG, "pcm play seat lock");
                            ble_audio_play((unsigned char *)(g_pcm_partition_info->base_addr +g_pcm_partition_info->start_addr), 800);
                        }
                    }

                    break;
                }
            
                case ALERTOR_CMD_MUTE_CTRL: {
                    if (alertor_data->param == ALERTOR_MUTE_ON) {
                        //静音开
                    } else if (alertor_data->param == ALERTOR_MUTE_OFF) {
                        //静音关
                    }

                    break;
                }
            
                default:
                    LOGE(TAG, "invaild cmd %d\n", alertor_data->cmd);
                    break;
            }
            ble_alertor_status_change(ALERTOR_ST_ACK);
        }
        break;
        case ALERTOR_ST_ACK: {
            if (g_conn_hanlde == -1) {
                return;
            }

            aos_timer_change(&ble_alertor_ctrl.timer, ble_alertor_ctrl.timeout * 1000);
            aos_timer_start(&ble_alertor_ctrl.timer);

            conn_param_t param = {
                0x06,
                0x06,
                0,
                100,
            };
            ble_stack_connect_param_update(g_conn_hanlde, &param);
            uint8_t send_buf[4]  = {0xff, 0x01, 0x01, 0xfe};
            ble_stack_gatt_notificate(g_conn_hanlde, g_yoc_alertor_handle + YOC_ALERTOR_IDX_TX_VAL, send_buf, sizeof(send_buf));
            // LOGI(TAG, "send ack %d, %s", sizeof(send_buf), bt_hex(send_buf, sizeof(send_buf)));

            ble_alertor_status_change(ALERTOR_ST_IDLE);

        }
        break;

        case ALERTOR_ST_DISCONNECT: {
            if (g_conn_hanlde == -1) {
                return;
            }

            ble_alertor_reset_rx();
            ble_alertor_status_change(ALERTOR_ST_IDLE);
            ble_stack_disconnect(g_conn_hanlde);
            break;
        }

        default:
            break;
    }

    ble_alertor_reset_rx();
}

static void ble_alertor_timeout(void *arg1, void *arg2)
{
    if (g_conn_hanlde != -1) {
        conn_param_t param = {
            0x06,
            0x06,
            0,
            100,
        };
        ble_stack_connect_param_update(g_conn_hanlde, &param);
        uint8_t send_buf[4]  = {0x11, 0x22, 0x33, 0x44};
        ble_stack_gatt_notificate(g_conn_hanlde, g_yoc_alertor_handle + YOC_ALERTOR_IDX_TX_VAL, send_buf, sizeof(send_buf));
        LOGI(TAG, "send ack %d, %s", sizeof(send_buf), bt_hex(send_buf, sizeof(send_buf)));
        aos_timer_start(&ble_alertor_ctrl.timer);
        return;
    }
    //ble_alertor_status_change(ALERTOR_ST_DISCONNECT);
    LOGW(TAG, "timeout");
}

int ble_alertor_init(alertor_event_callback_t cb)
{
    int ret;
    int s_flag;

    memset(&ble_alertor_ctrl, 0, sizeof(ble_alertor_ctrl));

    ble_stack_event_register(&ble_alertor_cb);

    ret = ble_stack_gatt_registe_service(&uart_service, uart_attrs, BLE_ARRAY_NUM(uart_attrs));

    if (ret < 0) {
        return 0;
    }

    g_yoc_alertor_handle = ret;

    ret = ble_stack_iocapability_set(IO_CAP_IN_NONE | IO_CAP_OUT_DISPLAY);

    if (ret < 0) {
        LOGE(TAG, "io set fail %d\n", ret);
        return ret;
    }

    ble_alertor_ctrl.st = ALERTOR_ST_IDLE;
    /* 
        超时设置, 连接成功开始第一包数据交互后, 开启连接超时,
        5秒没有操作,设备主动断开连接
    */
    ble_alertor_ctrl.timeout = 1;

    aos_timer_new_ext(&ble_alertor_ctrl.timer, ble_alertor_timeout, NULL, 1000, 0, 0);

    /* 注册回调函数 */
    ble_alertor_ctrl.cb = cb;

   ble_alertor_ctrl.bas_handle = bas_init(&ble_alertor_ctrl.bas);

    if (ble_alertor_ctrl.bas_handle == NULL) {
        LOGE(TAG, "BAS init FAIL!!!!");
        return -1;
    }

    ble_alertor_ctrl.dis_handle = dis_init(&dis_info);

    if (ble_alertor_ctrl.dis_handle == NULL) {
        LOGE(TAG, "DIS init FAIL!!!!");
        return -1;
    }

    ble_alertor_ctrl.hids_handle = hids_init(HIDS_REPORT_PROTOCOL_MODE);

    if (ble_alertor_ctrl.hids_handle == NULL)
    {
        LOGE(TAG, "HIDS init FAIL!!!!");
        return -1;
    }
	s_flag = set_data_map(report_map, sizeof(report_map), REPORT_MAP);

    if (s_flag == -1) {
        LOGE(TAG, "set_report_map FAIL!!!!");
        return s_flag;
    }

    s_flag = set_data_map((uint8_t *)(&send_data), sizeof(send_data), REPORT_INPUT);

    if (s_flag == -1) {
        LOGE(TAG, "set_report_input FAIL!!!!");
        return s_flag;
    }

    s_flag = set_data_map(report_output_data, sizeof(report_output_data), REPORT_OUTPUT);

    if (s_flag == -1) {
        LOGE(TAG, "set_report_output FAIL!!!!");
        return s_flag;
    }

    /* audio pcm player init */
    if (g_pcm_partition_handle < 0) {
        g_pcm_partition_handle = partition_open("audio");

        if (g_pcm_partition_handle < 0) {
            LOGE(TAG, "audio partition open fail %d", ret);
        }

        g_pcm_partition_info = hal_flash_get_info(g_pcm_partition_handle);
        if (g_pcm_partition_info == NULL) {
            LOGE(TAG, "audio partition info get fail");
            return -1;
        }

#if defined(BOARD_LE5010_EVB)
        drv_pinmux_config(PA08, PWM0_FUNC);
        drv_pinmux_config(PA09, PWM1_FUNC);
#elif defined(BOARD_PHY6220_EVB)
        drv_pinmux_config(P0, FMUX_PWM0);
        drv_pinmux_config(P7, FMUX_PWM1);
#endif
        aos_audio_pcm_init();
    }

    return 0;
}

